/* 
 *   AQ_HandCard class defines general properties 
 * of 2 hand cards, action cards and element cards. 
 * An AQ_HandCard have to be either ActionCard or 
 * ElementCard. It serves as a factory of cards, 
 * for code readability and flexibility.
 */
import java.io.Serializable;
import java.util.*;


class AQ_Element implements Comparable, Serializable
{
	public static final AQ_Element[] ELEMENT=new AQ_Element[]{new AQ_Element((byte)1), new AQ_Element((byte)2), new AQ_Element((byte)3), new AQ_Element((byte)4), new AQ_Element((byte)5)};
	
	public static final AQ_Element UNIVERSAL_ELEMENT=new AQ_Element((byte)6);
	
	public byte element;
	
	public AQ_Element(byte e)
	{
		element=e;
	}
	public int compareTo(Object o){
		if(o instanceof AQ_Element){
			AQ_Element x=(AQ_Element)o;
			return element-x.element;
		}
		else return 0;
	}
	public boolean equals(Object o)
	{
		if(o instanceof AQ_Element){
			AQ_Element x=(AQ_Element)o;
			return element==UNIVERSAL_ELEMENT.element || x.element==UNIVERSAL_ELEMENT.element || element==x.element;
		}
		else return false;
	}
	
	public String toString()
	{
		return Byte.toString(element);
	}
}

public abstract class AQ_HandCard implements Serializable
{
	public static class ActionCard extends AQ_HandCard
	{
		public static final byte[] ACTION=new byte[]{1, 2, 3, 4, 5, 6};
		
		private byte action_performed;
		
		public ActionCard(byte a)
		{
			action_performed=a;
		}
		
		public byte action()
		{
  
			return action_performed;
 
		}
 
		public boolean execute(AQ_Field field, AQ_Player[] player, final String[] args)
		{
			if(action_performed==1)
			{ // Trade Hands.
				if(args.length==1)
				{
					try
					{
						int i=Integer.parseInt(args[0]);
						if(player[i]!=owner)
						{
							owner.tradeHands(player[i]);
							return true;
						}
						else
							return false; // Trading with oneself is invalid.
					}
					catch(Exception e)
					{
						return false;
					}
				}
				else
					return false;
			}
			else if(action_performed==2)
			{ // Trade Goal.
				if(args.length==1)
				{
					int i=Integer.parseInt(args[0]);
					if(i<0)
						return field.tradeGoal(owner); // Trading with bank of gol cards.
					else if(player[i]!=owner)
					{
						owner.tradeGoal(player[i]);
						return true;
					}
					else 
						return false; // Trading with oneself is invalid.
				}
				else 
					return false;
			}
			else if(action_performed==3)
			{ // Move a Card.
				boolean rotation=false;
				int[] coordinate=new int[4];
				try
				{
					switch(args.length)
					{
						case 3: // When the length is odd, the command must include "-r" flag.
							if(args[0].equals("-r"))
							{
								coordinate[0]=coordinate[2]=Integer.parseInt(args[1]);
								coordinate[1]=coordinate[3]=Integer.parseInt(args[2]);
							}
							else if(args[2].equals("-r"))
							{
								coordinate[0]=coordinate[2]=Integer.parseInt(args[0]);
								coordinate[1]=coordinate[3]=Integer.parseInt(args[1]);
							}
							else 
								return false;
							rotation=true;
							break;
							
						case 4:
							for(int i=0; i<4; i++) 
								coordinate[i]=Integer.parseInt(args[i]);
							
							break;
							
						case 5:
							if(args[0].equals("-r")) 
								for(int i=0; i<4; i++) 
									coordinate[i]=Integer.parseInt(args[i+1]);
							
							else if(args[2].equals("-r"))
							{
								coordinate[0]=Integer.parseInt(args[0]);
								coordinate[1]=Integer.parseInt(args[1]);
								coordinate[2]=Integer.parseInt(args[3]);
								coordinate[3]=Integer.parseInt(args[4]);
							}
							else if(args[4].equals("-r"))
								for(int i=0; i<4; i++)
									coordinate[i]=Integer.parseInt(args[i]);
							rotation=true;
							break;
						default: return false;
					}
				}
				catch(Exception e)
				{
					return false;
				}
				
				return field.move(coordinate[0], coordinate[1], coordinate[2], coordinate[3], rotation);
			}
			else if(action_performed==4)
			{ // Rotate Goal.
				if(args==null || args.length==0)
				{
					field.addGoal(player[player.length-1].goal());
					for(int i=player.length-1; i>0; i--)
						player[i].setGoal(player[i-1].goal());
					
					player[0].setGoal(field.pollGoal());
					return true;
				}
				else
					return false;
			}
			else if(action_performed==5)
			{ // Zap a Card.
				if(args.length!=2) 
					return false;
				
				try
				{
					int[] coordinate=new int[2];
					ElementCard target=null;
					for(int i=0; i<2; i++) 
						coordinate[i]=Integer.parseInt(args[i]);
					
					target=field.zap(coordinate[0], coordinate[1], true);
					if(target!=null)
					{
						owner.add(target);
						return true;
					}
					else 
						return false;
				}	
				catch(Exception e)
				{
					return false;
				}
			}
			else if(action_performed==6)
			{ // Shuffle Hands.
				if(args==null || args.length==0)
				{
					LinkedList<AQ_HandCard> x=new LinkedList<AQ_HandCard>();
					
					for(AQ_Player p: player) 
						x.addAll(p.pollAll());
					
					Collections.shuffle(x);
					
					for(int i=Arrays.asList(player).indexOf(owner); !x.isEmpty(); i=(i+1)%player.length) 
						player[i].add(x.poll());
					
					return true;
				}
				else 
					return false;
			}
			return false;
		}
	}
	
	public static class ElementCard extends AQ_HandCard implements Cloneable, Comparable
	{
		private AQ_Element[] element;
		
		public ElementCard()
		{
			element=null; // Null array represents universal cards.
		}
		
		private ElementCard(AQ_Element e0, AQ_Element e1, AQ_Element e2, AQ_Element e3)
		{
			element=new AQ_Element[]{e0, e1, e2, e3};
		}
		
		public ElementCard clone()
		{
			if(element==null)
				return new ElementCard();
			else 
				return new ElementCard(element[0], element[1], element[2], element[3]);
		}
		
		public int compareTo(Object another)
		{
			if(another instanceof ElementCard)
			{
				ElementCard c=(ElementCard)another;
				if(element==null || c.element==null)
				{
					if(element==null && c.element==null) 
						return 0;
					else 
						return 1;
				}
				else if(upleft().equals(c.upleft()) && upright().equals(c.upright()) && downleft().equals(c.downleft()) && downright().equals(c.downright())) 
					return 0;
				
				else if(upleft().equals(c.downright()) && upright().equals(c.downleft()) && downleft().equals(c.upright()) && downright().equals(c.upleft()))
					return -1;
				
				else 
					return 1;
			}
			else return 1;
		}
		
		public boolean contains(final AQ_Element e)
		{
			if(element==null)
				return e.equals(AQ_Element.UNIVERSAL_ELEMENT);
			
			for(int i=0; i<element.length; i++) if(element[i].equals(e)) 
				return true;
			
			return false;
		}
		
		public boolean execute(AQ_Field field, AQ_Player[] player, final String[] args)
		{
			if(args.length!=2)
				return false;
			try
			{
				int[] coordinate=new int[2];
				int link=0;
				
				for(int i=0; i<2; i++)
					coordinate[i]=Integer.parseInt(args[i]);
				
				link=field.put(this, coordinate[0], coordinate[1], true);
				if(link>0)
				{
					link--;
					for(int i=0; i<link; i++)
					{
						AQ_HandCard c=field.pollBank();
						if(c!=null)
							owner.add(c);
					}
					owner=null;
					return true;
				}
				else 
					return false;
			}
			catch(Exception e)
			{
				return false;
			}
		}
		
		public AQ_Element upleft()
		{
			if(universal()) 
				return AQ_Element.UNIVERSAL_ELEMENT;
			
			else
				return element[0];
  
		}
		
		public AQ_Element upright()
		{
			if(universal())
				return AQ_Element.UNIVERSAL_ELEMENT;
			else
				return element[1];
		}
		
		public AQ_Element downleft()
		{
			if(universal()) 
				return AQ_Element.UNIVERSAL_ELEMENT;
			else 
				return element[2];
		}
		
		public AQ_Element downright()
		{
			if(universal())
				return AQ_Element.UNIVERSAL_ELEMENT;
			else 
				return element[3];
		}
		
		public boolean universal()
		{
			return element==null;
		}
		
		public void rotate()
		{ // A rotation by 180 degrees.
			if(!universal())
			{
				AQ_Element tmp=element[0];
				element[0]=element[3];
				element[3]=tmp;
				tmp=element[1];
				element[1]=element[2];
				element[2]=tmp;
			}
		}
	}
	
	public static ActionCard getActionCard(byte a)
	{
		return new ActionCard(a);
	}
	
	public static ElementCard getElementCard(byte e0, byte e1, byte e2, byte e3)
	{
		AQ_Element[] e=new AQ_Element[4];
		e[0]=new AQ_Element(e0);
		e[1]=new AQ_Element(e1);
		e[2]=new AQ_Element(e2);
		e[3]=new AQ_Element(e3);
		return new ElementCard(e[0], e[1], e[2], e[3]);
	}
	
	public static ElementCard getUniversalCard()
	{
		return new ElementCard();
	}
	
	protected AQ_Player owner; // Current owner.
	
	public abstract boolean execute(AQ_Field field, AQ_Player[] player, final String[] args);
	
	public AQ_Player getOwner()
	{
		return owner;
	}
	
	public void setOwner(AQ_Player player)
	{
		owner=player;
	}
}
